package vip.xiaomoli.courier.component.timewheel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vip.xiaomoli.courier.util.CourierConstant;

import javax.annotation.Nullable;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CourierTimeWheel {

	private static final Logger LOG = LoggerFactory.getLogger(CourierTimeWheel.class);

	private TaskSlotWheel slotWheel = new TaskSlotWheel();

	private ScheduledExecutorService wheelThreadPool = Executors.newSingleThreadScheduledExecutor(
			new TimeWheelThreadFactory("WheelThread") // 轮线程，扫槽数组
	);

	private ThreadPoolExecutor slotThreadPool = new ThreadPoolExecutor(
			CourierConstant.SLOT_THREAD_NUM, CourierConstant.SLOT_THREAD_NUM,
			0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
			new TimeWheelThreadFactory("SlotThread") // 槽线程，扫任务队列
	);

	private ThreadPoolExecutor taskThreadPool = new ThreadPoolExecutor(
			CourierConstant.TASK_THREAD_NUM, CourierConstant.TASK_THREAD_NUM,
			0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
			new TimeWheelThreadFactory("TaskThread") // 任务线程，执行任务
	);

	public CourierTimeWheel() {
		this.start();
	}

	public void addTask(CourierTask task, int delaySeconds) {
		slotWheel.add(task, delaySeconds);
	}

	private void start() {
		LOG.info("CourierTimeWheel Starting...");
		wheelThreadPool.scheduleWithFixedDelay(
				new TaskSlotLoader(slotWheel, slotThreadPool, taskThreadPool),
				1, 1, TimeUnit.SECONDS
		);
		LOG.info("CourierTimeWheel Started.");
	}

	static class TimeWheelThreadFactory implements ThreadFactory {
		private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private String namePrefix = CourierConstant.THREAD_NAME + "-";

		TimeWheelThreadFactory(String groupName) {
			SecurityManager s = System.getSecurityManager();
			group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
			namePrefix += groupName + "-pool" + POOL_NUMBER.getAndIncrement() + "-thread";
		}

		@Override
		public Thread newThread(@Nullable Runnable r) {
			Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
			if (t.isDaemon()) t.setDaemon(false);
			if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);
			return t;
		}
	}

}
